#include "variant.h"

// Default constructor creates an empty variant.
variant::variant() : valuetype(empty), dummypointer(nullptr) { }
// Copy constructor creates an identical variant.
variant::variant(const variant &v) : valuetype(v.getType()), dummypointer(nullptr) 
{
	switch(valuetype)
	{
	case booltype:
		boolvalue = v.toBool();
		return;
	case numerictype:
		numericvalue = new numeric(v.toNumeric());
		return;
	case rangetype:
		rangevalue = new numeric::range(v.toRange());
		return;
	case stringtype:
		stringvalue = new string(v.toString());
		return;
	case symboltype:
		symbolvalue = new symbol(v.toSymbol());
		return;
	case numericlisttype:
		numericlistvalue = new numeric::list(v.toNumericList());
		return;
	case listtype:
		listvalue = new list(v.toList());
		return;
	default:
		return;
	}
}
// Conversion constructor creates a booltype variant.
variant::variant(const bool &v) : valuetype(booltype), boolvalue(v) { }
// Conversion constructor creates a numerictype variant.
variant::variant(const size_t &v) : valuetype(numerictype), numericvalue(new numeric(v)) { *numericvalue = v; }
// Conversion constructor creates a numerictype variant.
variant::variant(const long &v) : valuetype(numerictype), numericvalue(new numeric(v)) { }		
// Conversion constructor creates a numerictype variant.
variant::variant(const double &v) : valuetype(numerictype), numericvalue(new numeric(v)) { }		
// Conversion constructor creates a numerictype variant.
variant::variant(const complex &v) : valuetype(numerictype), numericvalue(new numeric(v)) { }		
// Conversion constructor creates a numerictype variant.
variant::variant(const numeric &v) : valuetype(numerictype), numericvalue(new numeric(v)) { *numericvalue = v; }
// Conversion constructor creates a rangetype variant.
variant::variant(const numeric::range &v) : valuetype(rangetype), rangevalue(new numeric::range(v)) { }
// Conversion constructor creates a stringtype variant.
variant::variant(const string &v) : valuetype(stringtype), stringvalue(new string(v)) { }
// Conversion constructor creates a symboltype variant.
variant::variant(const symbol &v) : valuetype(symboltype), symbolvalue(new symbol(v)) { }
// Conversion constructor creates a numericlisttype variant.
variant::variant(const numeric::list &v) : valuetype(numericlisttype), numericlistvalue(new numeric::list(v)) { }
// Conversion constructor creates a listtype variant. Note lists of only numerics are converted to numericlists.
variant::variant(const variant::list &v)
{
	const size_t size = v.size(); 
	bool isnumericlist = __sec_reduce_and(v.data()[0:size].isNumericType()); 
	if(isnumericlist)
	{
		valuetype = numericlisttype;
		numericlistvalue = new numeric::list(size);
		numericlistvalue->data()[0:size] = v.data()[0:size].toNumeric();
	}
	else
	{
		valuetype = listtype;
		listvalue = new variant::list(v);
	}
}

// Conversion constructor creates a numericlisttype variant.
variant::variant(const std::vector<size_t> &v) : valuetype(numericlisttype), numericlistvalue(new numeric::list(v.size())) { const size_t size = v.size(); numericlistvalue->data()[0:size] = v.data()[0:size]; }
// Conversion constructor creates a numericlisttype variant.
variant::variant(const std::vector<long> &v) : valuetype(numericlisttype), numericlistvalue(new numeric::list(v.size())) { const size_t size = v.size(); numericlistvalue->data()[0:size] = v.data()[0:size]; }
// Conversion constructor creates a numericlisttype variant.
variant::variant(const std::vector<double> &v) : valuetype(numericlisttype), numericlistvalue(new numeric::list(v.size())) { const size_t size = v.size(); numericlistvalue->data()[0:size] = v.data()[0:size]; }
// Conversion constructor creates a numericlisttype variant.
variant::variant(const std::vector<complex> &v) : valuetype(numericlisttype), numericlistvalue(new numeric::list(v.size())) { const size_t size = v.size(); numericlistvalue->data()[0:size] = numeric(v.data()[0:size]); }

// Conversion constructor creates a numericlisttype variant.
variant::variant(const arma::vec2 &v) : valuetype(numericlisttype), numericlistvalue(new numeric::list(2)) { static const size_t size = 2; numericlistvalue->data()[0:size] = v.memptr()[0:size]; }
// Conversion constructor creates a numericlisttype variant.
variant::variant(const arma::vec3 &v) : valuetype(numericlisttype), numericlistvalue(new numeric::list(3)) { static const size_t size = 3; numericlistvalue->data()[0:size] = v.memptr()[0:size]; }
// Conversion constructor creates a numericlisttype variant.
variant::variant(const arma::ivec2 &v) : valuetype(numericlisttype), numericlistvalue(new numeric::list(2)) { static const size_t size = 2; numericlistvalue->data()[0:size] = (long)v.memptr()[0:size]; }
// Conversion constructor creates a numericlisttype variant.
variant::variant(const arma::ivec3 &v) : valuetype(numericlisttype), numericlistvalue(new numeric::list(3)) { static const size_t size = 3; numericlistvalue->data()[0:size] = (long)v.memptr()[0:size]; }
// Conversion constructor creates a numericlisttype variant.
variant::variant(const arma::ivec4 &v) : valuetype(numericlisttype), numericlistvalue(new numeric::list(4)) { static const size_t size = 4; numericlistvalue->data()[0:size] = (long)v.memptr()[0:size]; }
// Conversion constructor creates a numericlisttype variant.
variant::variant(const arma::vec &v) : valuetype(numericlisttype), numericlistvalue(new numeric::list(v.n_rows)) { const size_t size = v.n_rows; numericlistvalue->data()[0:size] = v.memptr()[0:size]; }
// Conversion constructor creates a numericlisttype variant.
variant::variant(const arma::ivec &v) : valuetype(numericlisttype), numericlistvalue(new numeric::list(v.n_rows)) { const size_t size = v.n_rows; numericlistvalue->data()[0:size] = (long)v.memptr()[0:size]; }
// Conversion constructor creates a numericlisttype variant.
variant::variant(const arma::uvec &v) : valuetype(numericlisttype), numericlistvalue(new numeric::list(v.n_rows)) { const size_t size = v.n_rows; numericlistvalue->data()[0:size] = (long)v.memptr()[0:size]; }
// Conversion constructor creates a numericlisttype variant.
variant::variant(const arma::cx_vec &v) : valuetype(numericlisttype), numericlistvalue(new numeric::list(v.n_rows)) { const size_t size = v.n_rows; numericlistvalue->data()[0:size] = numeric(v.memptr()[0:size]); }
// Conversion constructor creates a numericlisttype variant.
variant::variant(const arma::mat &v) : valuetype(listtype), listvalue(new variant::list(v.n_cols)) { const size_t size = v.n_cols; for(size_t c = 0; c < size; c++) { (*listvalue)[c] = arma::vec(v.col(c)); } }
// Conversion constructor creates a numericlisttype variant.
variant::variant(const arma::imat &v) : valuetype(listtype), listvalue(new variant::list(v.n_cols)) { const size_t size = v.n_cols; for(size_t c = 0; c < size; c++) { (*listvalue)[c] = arma::ivec(v.col(c)); } }
// Conversion constructor creates a numericlisttype variant.
variant::variant(const arma::cx_mat &v) : valuetype(listtype), listvalue(new variant::list(v.n_cols)) { const size_t size = v.n_cols; for(size_t c = 0; c < size; c++) { (*listvalue)[c] = arma::cx_vec(v.col(c)); } }

// Assignment operator creates an identical variant.
variant &variant::operator=(const variant &v)
{
	clear();

	valuetype = v.getType();

	switch(valuetype)
	{
	case booltype:
		boolvalue = v.toBool();
		break;
	case numerictype:
		numericvalue = new numeric(v.toNumeric());
		break;
	case rangetype:
		rangevalue = new numeric::range(v.toRange());
		break;
	case stringtype:
		stringvalue = new string(v.toString());
		break;
	case symboltype:
		symbolvalue = new symbol(v.toSymbol());
		break;
	case numericlisttype:
		numericlistvalue = new numeric::list(v.toNumericList());
		break;
	case listtype:
		listvalue = new list(v.toList());
		break;
	default:
		break;
	}
	
	return *this;
}

// Destructor frees all used memory.
variant::~variant() { clear(); }
// Frees all used memory.
void variant::clear()
{
	switch(valuetype)
	{
	case numerictype:
		if(dummypointer)
			delete numericvalue;
		break;
	case rangetype:
		if(dummypointer)
			delete rangevalue;
		break;
	case stringtype:
		if(dummypointer)
			delete stringvalue;
		break;
	case symboltype:
		if(dummypointer)
			delete symbolvalue;
		break;
	case numericlisttype:
		if(dummypointer)
			delete numericlistvalue;
		break;
	case listtype:
		if(dummypointer)
			delete listvalue;
		break;
	default:
		break;
	}
	valuetype = empty;
	dummypointer = nullptr;
}


// Type getter function.
variant::type variant::getType() const { return valuetype; }
// Tests for empty type, same as getType() == variant::empty.
bool variant::isEmpty() const { return valuetype == empty; }
// Tests for bool type, same as getType() == variant::booltype.
bool variant::isBoolType() const { return valuetype == booltype; }
// Tests for numeric type, same as getType() == variant::numerictype.
bool variant::isNumericType() const { return valuetype == numerictype; }
// Tests for numericrange type, same as getType() == variant::rangetype.
bool variant::isRangeType() const { return (valuetype == rangetype || (valuetype == numericlisttype && numericlistvalue->size() > 1 && numericlistvalue->size() < 4)); }
// Tests for string type, same as getType() == variant::stringtype.
bool variant::isStringType() const { return valuetype == stringtype; }
// Tests for symbol type, same as getType() == variant::symboltype.
bool variant::isSymbolType() const { return valuetype == symboltype; }
// Tests for variantlist type, same as getType() == variant::numericlisttype.
bool variant::isNumericListType() const { return valuetype == numericlisttype; }
// Tests for variantlist type, same as getType() == variant::listtype.
bool variant::isListType() const { return valuetype == listtype; }

// Explicit conversion to bool function.
bool variant::toBool() const { return (isBoolType()) ? boolvalue : false ; }
// Explicit conversion to numeric function.
numeric variant::toNumeric() const { return (isNumericType()) ? *numericvalue : numeric() ; }
// Explicit conversion to size_t.
size_t variant::toSize() const { return (isNumericType()) ? *numericvalue : 0 ; }
// Explicit conversion to long.
long variant::toLong() const { return (isNumericType()) ? *numericvalue : 0; }
// Explicit conversion to double.
double variant::toDouble() const { return (isNumericType()) ? *numericvalue : 0. ; }
// Explicit conversion to complex.
complex variant::toComplex() const { return (isNumericType()) ? *numericvalue : complex() ; }
// Explicit conversion to numericrange function.
numeric::range variant::toRange() const { return (isRangeType()) ? *rangevalue : ( (isListType() && listvalue->size() == 2) ? numeric::span(listvalue->at(0).toNumeric(),listvalue->at(1).toNumeric()) : ( (isListType() && listvalue->size() == 3) ? numeric::span(listvalue->at(0).toNumeric(),listvalue->at(1).toNumeric(),listvalue->at(2).toNumeric()) : numeric::range()  ) ) ; }
// Explicit conversion to string function.
string variant::toString() const { return (isStringType()) ? *stringvalue : string() ; }
// Explicit conversion to symbol function.
symbol variant::toSymbol() const { return (isSymbolType()) ? *symbolvalue : symbol() ; }
// Explicit conversion to numericlist.
numeric::list variant::toNumericList() const { return (isNumericListType()) ? *numericlistvalue : numeric::list() ; }
// Explicit conversion to variantlist function.
variant::list variant::toList() const { return (isListType()) ? *listvalue : list() ; }

// Explicit std::vector conversion.
	
// Explicit conversion to std::vector.
std::vector<size_t> variant::toStdSizeVector() const 
{ 
	if(!isNumericListType())
		return std::vector<size_t>();

	const size_t size = numericlistvalue->size();
	std::vector<size_t> result(size);
	result.data()[0:size] = (size_t)numericlistvalue->data()[0:size];
	return result;
}
// Explicit conversion to std::vector.
std::vector<long> variant::toStdLongVector() const
{ 
	if(!isNumericListType())
		return std::vector<long>();

	const size_t size = numericlistvalue->size();
	std::vector<long> result(size);
	result.data()[0:size] = (long)numericlistvalue->data()[0:size];
	return result;
}
// Explicit conversion to std::vector.
std::vector<double> variant::toStdDoubleVector() const
{ 
	if(!isNumericListType())
		return std::vector<double>();

	const size_t size = numericlistvalue->size();
	std::vector<double> result(size);
	result.data()[0:size] = (double)numericlistvalue->data()[0:size];
	return result;
}
// Explicit conversion to std::vector.
std::vector<complex> variant::toStdComplexVector() const
{ 
	if(!isNumericListType())
		return std::vector<complex>();

	const size_t size = numericlistvalue->size();
	std::vector<complex> result(size);
	result.data()[0:size] = (complex)numericlistvalue->data()[0:size];
	return result;
}

// Explicit Armadillo conversion.
	
// Explicit conversion to arma::vec.
arma::vec2 variant::toArmaDoubleVector2() const
{ 
	if(!isNumericListType())
		return arma::vec2();
	
	const size_t size = std::min<size_t>(2,numericlistvalue->size());
	arma::vec2 result(arma::fill::zeros);
	result.memptr()[0:size] = (double)numericlistvalue->data()[0:size];
	return result;
}
// Explicit conversion to arma::vec.
arma::vec3 variant::toArmaDoubleVector3() const
{ 
	if(!isNumericListType())
		return arma::vec3();
	
	const size_t size = std::min<size_t>(3,numericlistvalue->size());
	arma::vec3 result(arma::fill::zeros);
	result.memptr()[0:size] =(double) numericlistvalue->data()[0:size];
	return result;
}
// Explicit conversion to arma::vec.
arma::ivec2 variant::toArmaLongVector2() const
{ 
	if(!isNumericListType())
		return arma::ivec2();
	
	const size_t size = std::min<size_t>(2,numericlistvalue->size());
	arma::ivec2 result(arma::fill::zeros);
	result.memptr()[0:size] = (long)numericlistvalue->data()[0:size];
	return result;
}
// Explicit conversion to arma::vec.
arma::ivec3 variant::toArmaLongVector3() const
{ 
	if(!isNumericListType())
		return arma::ivec3();
	
	const size_t size = std::min<size_t>(3,numericlistvalue->size());
	arma::ivec3 result(arma::fill::zeros);
	result.memptr()[0:size] = (long)numericlistvalue->data()[0:size];
	return result;
}
// Explicit conversion to arma::vec.
arma::ivec4 variant::toArmaLongVector4() const
{ 
	if(!isNumericListType())
		return arma::ivec4();
	
	const size_t size = std::min<size_t>(4,numericlistvalue->size());
	arma::ivec4 result(arma::fill::zeros);
	result.memptr()[0:size] = (long)numericlistvalue->data()[0:size];
	return result;
}
// Explicit conversion to arma::vec.
arma::vec variant::toArmaDoubleVector() const
{ 
	if(!isNumericListType())
		return arma::vec();
	
	const size_t size = numericlistvalue->size();
	arma::vec result(size);
	result.memptr()[0:size] = (double)numericlistvalue->data()[0:size];
	return result;
}
// Explicit conversion to arma::vec.
arma::ivec variant::toArmaLongVector() const
{ 
	if(!isNumericListType())
		return arma::ivec();
	
	const size_t size = numericlistvalue->size();
	arma::ivec result(size);
	result.memptr()[0:size] = (double)numericlistvalue->data()[0:size];
	return result;
}
// Explicit conversion to arma::vec.
arma::uvec variant::toArmaSizeVector() const
{ 
	if(!isNumericListType())
		return arma::uvec();
	
	const size_t size = numericlistvalue->size();
	arma::uvec result(size);
	result.memptr()[0:size] = (size_t)numericlistvalue->data()[0:size];
	return result;
}
// Explicit conversion to arma::vec.
arma::cx_vec variant::toArmaComplexVector() const
{ 
	if(!isNumericListType())
		return arma::cx_vec();
	
	const size_t size = numericlistvalue->size();
	arma::cx_vec result(size);
	result.memptr()[0:size] = (complex)numericlistvalue->data()[0:size];
	return result;
}
